/**
 * Software License, Version 1.0
 * 
 * Copyright 2003 The Trustees of Indiana University. All rights reserved.
 * 
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license; 2) All redistributions in binary form
 * must reproduce the above copyright notice, this list of conditions and the
 * disclaimer listed in this license in the documentation and/or other materials
 * provided with the distribution; 3) Any documentation included with all
 * redistributions must include the following acknowledgement:
 * 
 * "This product includes software developed by the Community Grids Lab. For
 * further information contact the Community Grids Lab at
 * http://communitygrids.iu.edu/."
 * 
 * Alternatively, this acknowledgement may appear in the software itself, and
 * wherever such third-party acknowledgments normally appear.
 * 
 * 4) The name Indiana University or Community Grids Lab or NaradaBrokering,
 * shall not be used to endorse or promote products derived from this software
 * without prior written permission from Indiana University. For written
 * permission, please contact the Advanced Research and Technology Institute
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202. 5) Products
 * derived from this software may not be called NaradaBrokering, nor may Indiana
 * University or Community Grids Lab or NaradaBrokering appear in their name,
 * without prior written permission of ARTI.
 * 
 * 
 * Indiana University provides no reassurances that the source code provided
 * does not infringe the patent or any other intellectual property rights of any
 * other entity. Indiana University disclaims any liability to any recipient for
 * claims brought by any other entity based on infringement of intellectual
 * property rights or otherwise.
 * 
 * LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO
 * WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 * NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF
 * INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS.
 * INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS",
 * "VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.
 * LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR
 * ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION
 * GENERATED USING SOFTWARE.
 */
package cgl.narada.transport.tcp;

import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;

import cgl.narada.protocol.Protocol;
import cgl.narada.transport.TransmissionManager;
import cgl.narada.transport.TransmissionReporter;
import cgl.narada.transport.TransportDebugFlags;
import cgl.narada.transport.TransportException;

/**
 * Responsible for sending tcp packets across the socket connection that is
 * established during initializations. The TCPSenderThread notifies any loss of
 * connections to the TCPLink which in turn proceeds to noitify the
 * TCPLinkFactory.
 * 
 * @author Shrideep Pallickara $Revision$ $Date$
 */

public class TCPSenderThread implements TransportDebugFlags,
        TransmissionReporter {
    private Socket socket;

    private byte[] data;

    private DataOutputStream dout;

    private OutputStream out;

    private BufferedOutputStream bout;

    private TCPLink tcpLink;

    private int packetTracker = 0;

    private int lastPacketAcked = 0;

    private long ongoingTransfer = 0;

    private long timeOfLastTransfer = System.currentTimeMillis();

    private int sendBufferSize = 12 * 8192;

    private String moduleName = "TCPSenderThread: ";


    public TCPSenderThread(Socket socket, TCPLink tcpLink) {
        this.socket = socket;
        this.tcpLink = tcpLink;
        try {
            socket.setSoLinger(true, 2);
            socket.setTcpNoDelay(true);

            byte tc = 0x08;
            socket.setTrafficClass(tc);
            socket.setKeepAlive(true);
            socket.setSendBufferSize(sendBufferSize);
            if (TCPSenderThread_Debug) {
                System.out.println(moduleName + "Send Buffer Size ="
                    + socket.getSendBufferSize() + ", traffic class = "
                    + socket.getTrafficClass() + ", TcpNODelay="
                    + socket.getTcpNoDelay());
            }
        } catch (SocketException se) {
            se.printStackTrace();
        }

        try {
            out = socket.getOutputStream();
            // bout = new BufferedOutputStream(out);
            dout = new DataOutputStream(out);
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }


    /**
     * This method registers the transmission reporter to the transmission
     * manager.
     */
    private void registerTransmissionReporter(TCPLink tcpLink) {
        TransmissionManager transmissionManager =
            tcpLink.getTransportHandler().getTransmissionManager();
        if (transmissionManager != null) {
            transmissionManager.registerTransmissionReporter(tcpLink
                .getLinkId(), this);
        } else {
            System.out.println(moduleName + "Transmission's manager is NULL");
        }
    }


    public void setLinkId(String linkId) {
        registerTransmissionReporter(tcpLink);
    }


    public void releaseResources() {
        if (TCPSenderThread_Debug)
            System.out.println(moduleName + "Closing " + socket);
        try {
            socket.getInputStream().close();
            socket.close();
        } catch (IOException ioe) {
            System.out.println(moduleName + "Problem while releasing resources"
                + ioe);
        }
    }


    public void sendData(byte[] dataToSend) throws TransportException {
        ongoingTransfer += dataToSend.length;

        // long startTime = System.currentTimeMillis();
        performAtomicTransfer(dataToSend);
        // long elapsedTime = System.currentTimeMillis() - startTime;
        // String report = "\nTransfer time=" + elapsedTime;
        // System.out.println(report);

        ongoingTransfer -= dataToSend.length;
    }


    public void performAtomicTransfer(byte[] dataToSend)
        throws TransportException {
        if (TCPSenderThread_Debug)
            System.out.println(moduleName + "Sending data on link "
                + tcpLink.getLinkId());

        try {
            int dataLength = dataToSend.length;

            if (dataLength <= 0) {
                System.out
                    .println("\n **TCPSenderThread: Data length <= 0 *** \n");
                return;
            }

            packetTracker++;

            dout.write(Protocol.DATA);
            dout.writeInt(dataLength);
            // long startTime = System.currentTimeMillis();

            dout.write(dataToSend, 0, dataLength);
            dout.flush();

            // long elapsedTime = System.currentTimeMillis() - startTime;
            // String report = "\nTransfer time=" + elapsedTime;
            // System.out.println(report);

            if (TCPSenderThread_Debug) {
                System.out.println(moduleName + "Sent data on link "
                    + tcpLink.getLinkId() + "PacketNumber=" + packetTracker
                    + " , size=" + dataLength);
            }
        } catch (java.io.IOException ioe) {
            String errorInfo = moduleName + "sending data " + ioe;
            System.out.println(errorInfo);
            throw new TransportException(errorInfo);
        }
    }


    public void sendData(byte[] dataToSend, String dataType)
        throws TransportException {
        /** NBNI */
        System.out.println(moduleName + "SHOULD THIS BE CALLED?");
        ongoingTransfer += dataToSend.length;
        sendData(dataToSend);
        ongoingTransfer -= dataToSend.length;
    }


    public void sendPing() {
        /**
         * try { dout.writeByte(Protocol.PING);
         * dout.writeLong(System.currentTimeMillis()); dout.flush(); } catch
         * (java.io.IOException ioe) { System.out.println("Lost connection to " +
         * socket ); }
         */
    }


    public void sendPingResponse(long timeSent) {
        /**
         * try { dout.writeByte(Protocol.PING_REPLY); dout.writeLong(timeSent);
         * dout.flush(); } catch (java.io.IOException ioe) {
         * System.out.println("Lost connection to " + socket ); }
         */
    }


    public void run() {

    }


    public boolean hasPendingTransfer() {
        if (ongoingTransfer != 0) {
            return true;
        }
        return false;
    }


    public long getSizeOfOngoingTransfer() {
        return ongoingTransfer;
    }

}
